using System;
using System.Xml;
using System.IO;

using DataStreams.Csv;

namespace Logger
{
    /// <summary>
    /// The logger class which is responsible for the
    /// task logging and reporting functionality
    /// </summary>
    public class TaskLogger
    {
        private readonly string _logFileName;
        private Task _currentTask;
        private XmlWriter _writer;
        private XmlWriterSettings _settings;
        private StreamWriter _fileWriter;
        /// <summary>
        /// To denote Report types
        /// </summary>
        public enum Report
        {
            ///<summary>
            /// To denote the report is for the current day
            ///</summary>
            Today, 
            ///<summary>
            /// To denote the report is for a range of days
            ///</summary>
            Range
        }

        /// <summary>
        /// Parameterized constructor with the intermediate filename parameter
        /// </summary>
        /// <param name="fileName">Intermediate filename</param>
        public TaskLogger(string fileName)
        {
            _logFileName = fileName;
            _currentTask = null;
            if(!File.Exists(_logFileName))
                File.CreateText(_logFileName);
        }

        /// <summary>
        /// To check if there is any current task at hand
        /// </summary>
        /// <returns>Whether task is available/active or not</returns>
        public bool IsTaskAvailable()
        {
            return (_currentTask != null);
        }

        /// <summary>
        /// Start a new task
        /// </summary>
        /// <param name="taskName">The name of the task at hand</param>
        /// <param name="startTime">Time when the task is started</param>
        /// <returns>Whether task start is successful or not</returns>
        public bool StartNewTask(string taskName, DateTime startTime)
        {
            if (!IsTaskAvailable())
            {
                _currentTask = new Task(taskName, startTime);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Pause the current task
        /// </summary>
        /// <param name="pauseTime">Time when the activity is paused</param>
        /// <returns>Whether or not the pause operation is successful</returns>
        public bool PauseCurrentTask(DateTime pauseTime)
        {
            if (IsTaskAvailable())
            {
                _currentTask.EndTime = pauseTime;
                return WriteTaskToXml();
            }
            return false;
        }

        /// <summary>
        /// Resume current task
        /// </summary>
        /// <param name="resumeTime">Time when the activity is resumed</param>
        /// <returns>Whether or not the resume operation is successful</returns>
        public bool ResumeCurrentTask(DateTime resumeTime)
        {
            if (IsTaskAvailable())
            {
                _currentTask.StartTime = resumeTime;
                return true;
            }
            return false;
        }

        /// <summary>
        /// End the task at hand
        /// </summary>
        /// <param name="endTime">Time when the activity is ended</param>
        /// <returns>Whether or not the end operation is successful</returns>
        public bool EndCurrentTask(DateTime endTime)
        {
            if (IsTaskAvailable())
            {
                _currentTask.EndTime = endTime;
                if(!WriteTaskToXml())
                {
                    return false;
                }
                _currentTask = null;
                return true;
            }
            return false;
        }

        /// <summary>
        /// The core functionality which writes the task to the
        /// intermediate xml file when the task has ended
        /// </summary>
        /// <returns>Whether or not the write function is successful</returns>
        private bool WriteTaskToXml()
        {
            if(IsTaskAvailable())
            {
                StartLogger();
                if(_writer == null)
                {
                    return false;
                }
                _writer.WriteStartElement("task");
                _writer.WriteStartElement("name");
                _writer.WriteString(_currentTask.Name);
                _writer.WriteEndElement();
                _writer.WriteStartElement("start-time");
                _writer.WriteString(_currentTask.StartTime.ToString());
                _writer.WriteEndElement();
                _writer.WriteStartElement("end-time");
                _writer.WriteString(_currentTask.EndTime.ToString());
                _writer.WriteEndElement();
                _writer.WriteEndElement();
                
                StopLogger();
                return true;
            }
            return false;
        }

        /// <summary>
        /// Start the logging process
        /// </summary>
        private void StartLogger()
        {
            _settings = new XmlWriterSettings();
            _settings.Indent = true;
            _settings.OmitXmlDeclaration = true;
            _settings.ConformanceLevel = ConformanceLevel.Auto;
            _settings.NewLineHandling = NewLineHandling.Replace;
            _fileWriter = new StreamWriter(_logFileName, true);
            _writer = XmlWriter.Create(_fileWriter, _settings);
        }

        /// <summary>
        /// Stop the loggin process
        /// </summary>
        private void StopLogger()
        {
            _writer.Flush();
            _fileWriter.Close();

            _settings = null;
            _fileWriter = null;
            _writer = null;
        }

        /// <summary>
        /// Cancel the task at hand
        /// </summary>
        public void CancelTask()
        {
            _currentTask = null;
        }

        /// <summary>
        /// Core reporting module
        /// </summary>
        /// <param name="csvName">The file to which the report has to be generated</param>
        /// <param name="reportOption">Enum specifying whether the report option is for the current day or for a range of days</param>
        /// <param name="start">Start day for the report</param>
        /// <param name="end">End day for the report</param>
        public bool ExportToCsv(string csvName, Report reportOption, DateTime start, DateTime end)
        {
            if(File.ReadAllBytes(_logFileName).Length == 0)
                return false;

            CsvWriter writer = new CsvWriter(csvName);

            writer.Write("Task name");
            writer.Write("Start time");
            writer.Write("End time");
            writer.Write("Duration");
            writer.EndRecord();

            if(reportOption == Report.Today)
            {
                start = end = DateTime.Today;
            }
            /* Changed for issue 6
             * Date ranges are inclusive.
             * If 1.0 is not added, the end
             * date will not be included in the
             * report */
            end = end.AddDays(1.0);

            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ConformanceLevel = ConformanceLevel.Auto;
            StreamReader streamReader = new StreamReader(_logFileName);
            XmlReader reader = XmlReader.Create(streamReader, settings);

            while(!reader.EOF)
            {
                reader.ReadStartElement();
                string taskName = reader.ReadElementString();
                DateTime startTime = Convert.ToDateTime(reader.ReadElementString());
                DateTime endTime = Convert.ToDateTime(reader.ReadElementString());
                if (startTime > start && startTime < end)
                {
                    TimeSpan duration = endTime - startTime;
                    writer.Write(taskName);
                    writer.Write(startTime.ToString());
                    writer.Write(endTime.ToString());
                    writer.Write(duration.TotalHours.ToString());
                    writer.EndRecord();
                }
                reader.ReadEndElement();
            }

            reader.Close();
            streamReader.Close();
            writer.Close();

            return true;
        }
    }

    /// <summary>
    /// Task class
    /// </summary>
    public class Task
    {
        private readonly string _name;
        private DateTime _startTime;
        private DateTime _endTime;
        private bool _isActive;

        /// <summary>
        /// Name of the task
        /// </summary>
        public string Name
        {
            get { return _name; }
        }

        /// <summary>
        /// Task start time
        /// </summary>
        public DateTime StartTime
        {
            get { return _startTime; }
            set { _startTime = value; }
        }

        /// <summary>
        /// Whether or not the task is active
        /// </summary>
        public bool IsActive
        {
            get { return _isActive; }
            set { _isActive = value; }
        }

        /// <summary>
        /// Task end time
        /// </summary>
        public DateTime EndTime
        {
            get { return _endTime; }
            set { _endTime = value; }
        }

        /// <summary>
        /// Parameterized constructor
        /// </summary>
        /// <param name="name">Name of the task</param>
        /// <param name="startTime">Time when the task is started</param>
        public Task(string name, DateTime startTime)
        {
            _name = name;
            StartTime = startTime;
        }
    }
}